/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package GraphPackage;

import GUIUtilities.LabelManager;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Polygon;
import java.util.Vector;
import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 *
 * @author Heather
 */
public class LineGraph extends Graph implements GraphPolygonGrabberInterface
{

  // the base line thickness is how thick all lines are at the very least
  private int baseLineThickness = 4;
  private LineGraphLines linesPanel;
  private Image buffer;
  private Graphics bufferG;
  private int numberOfYAxisValuesExcludingZero = 10;
  private int numberOfXAxisValuesExcludingZero = 10;
  // The set of lines
  private Vector<Vector<Double>> lines = new Vector<Vector<Double>>();
  // The thicknesses of the lines
  private Vector<Vector<Integer>> thicknesses = new Vector<Vector<Integer>>();
  // The color of each line
  private Vector<Color> lineColors = new Vector<Color>();
  // stores the polygons that represent the lines
  private Vector<Vector<Polygon>> linesPolygons = new Vector<Vector<Polygon>>();

  public LineGraph()
  {
    this.setBackground(bgColor);

    // setup the bars panel
    linesPanel = new LineGraphLines();

    this.setLayout(null);
    add(linesPanel);
    linesPanel.setLayout(null);
    linesPanel.setSize(getSize());
    linesPanel.setLocation(0, 0);

    setDefaultLabelTexts();
    setupLabelLocations();
    setupComponentOrder();
  }

  protected void calculateVariables()
  {
    super.calculateVariables();
    maxDimension = graphHeight * (Math.abs(maxValue) / (Math.abs(minValue) + Math.abs(maxValue)));
    minDimension = graphHeight * (Math.abs(minValue) / (Math.abs(minValue) + Math.abs(maxValue)));
    maxDimension = getSize().height - bottomLabelBorder - topLabelBorder;
  }

  public void setDefaultLabelTexts()
  {

    for (JLabel xAxisLabel : xAxisLabels)
    {
      xAxisLabel.setText("bar");
    }

    createYAxisLabels();
    graphTitleLabel.setText("Graph Title");

    setupLabelLocations();
    setupComponentOrder();
  }

  private void createYAxisLabels()
  {
    yAxisLabels.clear();
    // first calculate how many labels will be able to fit based
    // on the maximum bar height
    calculateVariables();

    int valueIncrement = (int) (maxValue / numberOfYAxisValuesExcludingZero);
    //int heightIncrement = (int) ((valueIncrement / maxValue) * maxValueDimension);
    // start placing labels starting from the top
    for (int i = 0; i <= numberOfYAxisValuesExcludingZero; i++)
    {
      double value = maxValue - (i * valueIncrement);
      JLabel newYLabel = new JLabel("" + value);
      Dimension d = LabelManager.getLabelDimensions(newYLabel);
      newYLabel.setSize(d);
      newYLabel.setPreferredSize(d);
      yAxisLabels.add(newYLabel);
      add(newYLabel);
    }
  }

  public void setupLabelLocations()
  {
    // these represent the locations of the labels.
    int x = 0;
    int y = 0;

    // placing the top title label
    x = (getSize().width / 2) - (graphTitleLabel.getSize().width / 2);
    y = (int) topLabelBorder / 2;
    graphTitleLabel.setLocation(x, y);
    Dimension titleD = LabelManager.getLabelDimensions(graphTitleLabel);
    graphTitleLabel.setSize(titleD);
    graphTitleLabel.setPreferredSize(titleD);

    // place the yAxisLabels
    calculateVariables();

    int valueIncrement = (int) (maxValue / numberOfYAxisValuesExcludingZero);
    int heightIncrement = (int) ((valueIncrement / maxValue) * maxDimension);
    // start placing labels starting from the top
    for (int i = 0; i <= numberOfYAxisValuesExcludingZero; i++)
    {
      JLabel currentYLabel = yAxisLabels.get(i);//numberOfYAxisValues - 1 - i);
      Dimension d = LabelManager.getLabelDimensions(currentYLabel);
      x = (int) leftLabelBorder - d.width;
      y = (int) topLabelBorder + (heightIncrement * i) - d.height / 2;
      currentYLabel.setLocation(x, y);
    }
  }

  public void setupComponentOrder()
  {
    removeAll();

    for (int i = 0; i < xAxisLabels.size(); i++)
    {
      add(xAxisLabels.get(i));
    }

    for (int i = 0; i < yAxisLabels.size(); i++)
    {
      add(yAxisLabels.get(i));
    }

    add(graphTitleLabel);
    add(linesPanel);
  }

  // adds a line.  the vector is the set of heights.
  // returns true if successful, false if not successful
  public boolean addLine(Vector<Double> newLine, Vector<Integer> lineThicknesses, Color c)
  {
    boolean valid = true;
    // first we check the validity of the line
    if (newLine.size() != numberOfXAxisValuesExcludingZero)
    {
      System.out.println("the added line does not have the right amount of values");
      valid = false;
    }

    // check that all values are within the max value
    for (Double d : newLine)
    {
      if (d > maxValue)
      {
        System.out.println("a value is too large");
        valid = false;
      }
      if (d < minValue)
      {
        System.out.println("a value is too small");
        valid = false;
      }
    }

    // the thicknesses need to be one less than the points forming the line b/c
    // thicknesses describe the space between points
    if (newLine.size() != (lineThicknesses.size() + 1))
    {
      System.out.println("thicknesses not proper length");
      valid = false;
    }

    if (valid)
    {
      lines.add(newLine);
      thicknesses.add(lineThicknesses);
      lineColors.add(c);
    }

    return valid;
  }

  public void addRandomLine()
  {
    Vector<Double> newLine = new Vector<Double>();
    for (int i = 0; i < numberOfXAxisValuesExcludingZero; i++)
    {
      Double nextLineValue = randGen.nextDouble() * maxValue;
      newLine.add(nextLineValue);
    //System.out.println("max value is: " + maxValue);
    //newLine.add(maxValue);
    }

    Vector<Integer> lineThickness = new Vector<Integer>();
    for (int i = 0; i < numberOfXAxisValuesExcludingZero - 1; i++)
    {
      int thickness = randGen.nextInt(8);
      lineThickness.add(thickness);
    //lineThickness.add(8);
    }

    Color c = new Color(randGen.nextInt(200), randGen.nextInt(200), randGen.nextInt(200));

    boolean b = addLine(newLine, lineThickness, c);
    System.out.println("success at adding random line: " + b);
  }

  public void setSize(Dimension d)
  {
    super.setSize(d);
    calculateVariables();
    buffer = null;
    linesPanel.setSize(d);
    setupLabelLocations();
    repaint();
  }

  public void setSize(int x, int y)
  {
    super.setSize(x, y);
    calculateVariables();
    buffer = null;
    linesPanel.setSize(x, y);
    setupLabelLocations();
    repaint();
  }

  public void setPreferredSize(Dimension d)
  {
    super.setPreferredSize(d);
    buffer = null;
    linesPanel.setPreferredSize(d);
    repaint();
  }

  public Vector<Polygon> getPolygons()
  {
    Vector<Polygon> ret = new Vector<Polygon>();
    for (Vector<Polygon> v : linesPolygons)
    {
      for (Polygon p : v)
      {
        ret.add(p);
      }
    }

    return ret;
  }

  /**
   * This is the rectangle stuff that needs to be drawn for bar graphs
   * @author Heather
   */
  private class LineGraphLines extends JPanel
  {

    public LineGraphLines()
    {
      super();
    }

    public void drawGraph()
    {
      // clear the window and setup the graphics objects
      bufferG.setColor(bgColor);
      bufferG.fillRect(0, 0, getSize().width, getSize().height);
      Graphics windowG = getGraphics();

      // fill in the graph bg
      Color graphBGColor = Color.white;
      bufferG.setColor(graphBGColor);
      windowG.setColor(graphBGColor);
      bufferG.fillRect((int) leftLabelBorder, (int) topLabelBorder, (int) graphWidth, (int) maxDimension);

      // start filling the lines in
      // note that the denominator is n-1 b/c there are n-1 gaps between n points
      int xInterval = (int) (getWidth() - leftLabelBorder - rightBorder) / (numberOfXAxisValuesExcludingZero - 1);
      for (int i = 0; i < lines.size(); i++)
      {
        Vector<Polygon> linePolys = new Vector<Polygon>();
        Vector<Double> lineValues = lines.get(i);
        Vector<Integer> lineThickness = thicknesses.get(i);
        int startY = (int) (maxDimension - (lineValues.get(0) / maxValue) * maxDimension) + (int) topLabelBorder;

        // for each point in the line
        System.out.println("A LINE");
        for (int j = 0; j < lineValues.size() - 1; j++)
        {
          int thickness = lineThickness.get(j);

          int endY = (int) (maxDimension - (lineValues.get(j + 1) / maxValue) * maxDimension) + (int) topLabelBorder;
          int startX = (j * xInterval) + (int) leftLabelBorder;
          int endX = ((j + 1) * xInterval) + (int) leftLabelBorder;
          double slope = (double) (endY - startY) / (double) (endX - startX);
          if (endY == startY)
          {
            System.out.println("same at: " + j + " WHAT THE FUCK?!");
          }
          double inverseSlope = -(1 / slope);
          System.out.println("slope: " + slope);

          Polygon p = new Polygon();

          int halfThickness = (thickness + baseLineThickness) / 2;
          double theta = Math.atan(Math.abs(inverseSlope));
          //double theta = Math.atan(inverseSlope);
          double xOffset = (halfThickness * Math.cos(theta));
          double yOffset = (halfThickness * Math.sin(theta));
          System.out.println("line angle is: " + Math.atan(slope));

          if (slope < 0)
          {
            p.addPoint(startX - (int) xOffset, startY - (int) yOffset);
            p.addPoint(endX - (int) xOffset, endY - (int) yOffset);
            p.addPoint(endX + (int) xOffset, endY + (int) yOffset);
            p.addPoint(startX + (int) xOffset, startY + (int) yOffset);
          }
          else
          {
            if (slope > 0)
            {
              p.addPoint(startX + (int) xOffset, startY - (int) yOffset);
              p.addPoint(endX + (int) xOffset, endY - (int) yOffset);
              p.addPoint(endX - (int) xOffset, endY + (int) yOffset);
              p.addPoint(startX - (int) xOffset, startY + (int) yOffset);
            }
            else
            {
              p.addPoint(startX, startY + halfThickness);
              p.addPoint(endX, endY + halfThickness);
              p.addPoint(endX, endY - halfThickness);
              p.addPoint(startX, startY - halfThickness);
            }
          }

          linePolys.add(p);

          bufferG.setColor(lineColors.get(i));
          windowG.setColor(lineColors.get(i));
          bufferG.fillPolygon(p);
          windowG.fillPolygon(p);

          // lastly, make the last pt to the new start
          startY = endY;
        }

        linesPolygons.add(linePolys);
      }

      // draw the xAxis
      int xAxisH = 2;
      bufferG.setColor(Color.black);
      windowG.setColor(Color.black);
      bufferG.fillRect((int) leftLabelBorder, (int) (maxDimension + topLabelBorder), (int) (getSize().width - leftLabelBorder - rightBorder), xAxisH);
      windowG.fillRect((int) leftLabelBorder, (int) (maxDimension + topLabelBorder), (int) (getSize().width - leftLabelBorder - rightBorder), xAxisH);

      // draw the yAxis
      int yAxisW = 2;
      bufferG.setColor(Color.black);
      windowG.setColor(Color.black);
      bufferG.fillRect((int) leftLabelBorder, (int) topLabelBorder, (int) yAxisW, (int) maxDimension);
      windowG.fillRect((int) leftLabelBorder, (int) topLabelBorder, (int) yAxisW, (int) maxDimension);
    }

    public void paint(Graphics g)
    {
      // This creates the buffer
      if (buffer == null)
      {
        int xBound = getSize().width;
        int yBound = getSize().height;

        buffer = this.createImage(xBound, yBound);
        bufferG = buffer.getGraphics();
        bufferG.setColor(Color.white);
        bufferG.fillRect(0, 0, xBound, yBound);
      }
      g.drawImage(buffer, 0, 0, this);
      drawGraph();
    }
  }
}
